Latest update: August 2013
In this tutorial, we will show you how to get thumbnail images of files on your FlashAir. We will use thumbnail.cgi and command.cgi to do this. This tutorial builds off of Android Tutorial 3: Downloading Content.
We will pair file names with their corresponding thumbnail images and show them in an interactive content list. Upon starting the application, the root directory will be displayed. The list will look like this:
If you click an image file in the content list, it should display the image in another screen:
Upon clicking on a directory in the content list, that child directory will open and display its contents:
If you click an image file in the child directory, it should display the image in another screen:
As you are viewing the image, the image file will download to your Android device.
This tutorial will take some of the commands learned in previous tutorials and use them to obtain or display data.
We will need to create the following files in order to write this application:
Important: Please note that your project contains a
file called
AndroidManifest.xml. This file gives your application particular permissions. By
default,
applications are not permitted to access the internet. The path to this file should look
something
like:
[Project_Folder]/AndroidManifest.xml
You will need to add the following lines of code into your
AndroidManifest.xml in order for this application to work:
<uses-permission android:name="android.permission.INTERNET" />
First, we will write the activity_main.xml file that determines the layout of our Android App. This can be found in your layout folder. The path to this file should look something like: [Project_Folder]/res/layout/activity_main.xml
This file will be identical to the activity_main.xml file from Android Tutorial 3: Downloading Content. Please refer to that tutorial for an explanation of the implementation.
Next, we will create the activity_image_view.xml file that determines the layout of our image viewing screen. This can also be found in your layout folder. The path to this file should look something like: [Project_Folder]/res/layout/activity_image_view.xml
This file will be identical to the activity_image_view.xml file from Android Tutorial 3: Downloading Contents. Please refer to that tutorial for an explanation of the implementation.
Now we will modify the
MainActivity.java file. We will start by getting a list of the file names in the
desired
directory of your FlashAir device (see
Android Tutorial 3: Downloading Contents
for a detailed explanation). Then we will get a
thumbnail image for each file in the list and will set the list to display both the file
name
and thumbnail pair in a
ListView
format. We will then establish the click behavior of the list.
The content list should not only be clickable, but specifically be able to recognize which
element
in the list is being clicked and subsequently have that item display itself. We will have
to
implement our own version of the
onItemClick()
function to do this, so we will start by modifying the class
declaration.
We want our content list to appear when the application opens, so we will also modify the
onCreate(Bundle savedInstanceState)
function that initializes the
Activity
class.
Although this code is very similar to that in previous tutorials, there are some critical
differences.
If you are reusing code from the previous tutorial, please be sure to check that your
variable
types and function calls match this new tutorial code. We will write the reason using
SimpleAdapter
for this function later in this tutorial.
public class MainActivity extends Activity implements AdapterView.OnItemClickListener {
ListView listView;
ImageView imageView;
TextView currentDirText;
TextView numFilesText;
Button backButton;
String rootDir = "DCIM";
String directoryName = rootDir; // Initialize to rootDirectory
SimpleAdapter listAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
// Set buttons
getWindow().setTitleColor(Color.rgb(65, 183, 216));
backButton = (Button)findViewById(R.id.button1);
backButton.getBackground().setColorFilter(Color.rgb(65, 183, 216), PorterDuff.Mode.SRC_IN);
backButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(directoryName.equals(rootDir)) {
listRootDirectory();
}
else {
int index = directoryName.lastIndexOf("/");
directoryName = directoryName.substring(0, index);
listDirectory(directoryName);
}
}
});
backButton.setEnabled(false); // Disable in root directory
listRootDirectory();
}
catch(Exception e) {
Log.e("ERROR", "ERROR: " + e.toString());
e.printStackTrace();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
We are using a
ListView
to view our thumbnail and file name pairs. However, since we are
fetching each
image from the FlashAir device, and do not have it as a static
Drawable
in our project folder, our application will have to process each
thumbnail image
at runtime. We will have to use an
Adapter
to handle the thumbnail images. Since we need the thumbnail images to
be associated
with particular file names, we have chosen to use a
SimpleAdapter
, which is declared above. We need to create a custom
ViewBinder
so that the
SimpleAdapter
will correctly map the thumbnails and file names to their
respective views.
class CustomViewBinder implements ViewBinder {
@Override
public boolean setViewValue(View view, Object obj, String text) {
if((view instanceof ImageView) && (obj instanceof Drawable)) {
ImageView imageView = (ImageView) view;
BitmapDrawable thumbnail = (BitmapDrawable) obj;
imageView.setImageDrawable((Drawable)thumbnail);
return true;
}
return false;
}
}
We will use the following CGI command to get the number of items in each directory:
command.cgi
with
op=101
and the directory as a parameter
http://flashair/command.cgi?op=101&DIR=/DCIM
<NumberofItems>
We will use the following command to retrieve a list of contents from the FlashAir device:
command.cgi
with
op=100
and the directory as a parameter
http://flashair/command.cgi?op=100&DIR=/DCIM
<Directory>,<Filename>,<Size>,<Attribute>,<Date>,<Time>
We will use the following CGI command to get the thumbnail of each image file:
thumbnail.cgi
with the file path as a parameter
http://flashair/thumbnail.cgi?directoryPath/fileName
<EXIF_thumbnail_image>
We will set each thumbnail to be a value for key
"thmb"
and each file name to be a value for key
"fname"
in a
Map
. This
Map
will be added to an
ArrayList
, which will be used by the
SimpleAdapter
above. We will read the file into a
Bitmap
object.
To execute this CGI command, we will reuse the FlashAirRequest.java file from Android Tutorial 3: Downloading Contents:
The
listRootDirectory()
and
listDirectory(directoryName)
functions called in the
onCreate(Bundle savedInstanceState)
function are the functions that handle
requesting
and fetching content data from the FlashAir device and displaying it to the screen. The
implementation
for these functions are explained in
Android Tutorial 3: Downloading Contents.
Please refer to that tutorial for additional detail.
public void listRootDirectory() {
directoryName = rootDir;
listDirectory(directoryName);
}
public void listDirectory(String dir) {
// Prepare command directory path
if(dir.equals(rootDir)) {
backButton.setEnabled(false);
}
else {
backButton.setEnabled(true);
}
currentDirText = (TextView)findViewById(R.id.textView1);
currentDirText.setText(dir + "/");
dir = "/" + dir;
ArrayList <NameValuePair> httpParams = new ArrayList <NameValuePair> ();
httpParams.add(new BasicNameValuePair("DIR", dir));
dir = URLEncodedUtils.format (httpParams, "UTF-8" );
numFilesText = (TextView)findViewById(R.id.textView2);
// Fetch number of items in directory and display in a TextView
new AsyncTask<String, Void, String>(){
@Override
protected String doInBackground(String... params) {
String dir = params[0];
String fileCount = FlashAirRequest.getString("http://flashair/command.cgi?op=101&" + dir);
return fileCount;
}
@Override
protected void onPostExecute(String fileCount) {
numFilesText.setText("Items Found: " + fileCount);
}
}.execute(dir);
// Fetch list of items in directory and display in a ListView
new AsyncTask<String, Void, ListAdapter>(){
@Override
protected ListAdapter doInBackground(String... params) {
String dir = params[0];
ArrayList <String> fileNames = new ArrayList <String>();
String files = FlashAirRequest.getString("http://flashair/command.cgi?op=100&" + dir);
String[] allFiles = files.split("([,\n])"); // split by newline or comma
for(int i = 2; i < allFiles.length; i= i + 6) {
if(allFiles[i].contains(".")) {
// File
fileNames.add(allFiles[i]);
}
else { // Directory, append "/"
fileNames.add(allFiles[i] + "/");
}
}
// Get thumbnails
ArrayList<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
for(int i = 0; i < fileNames.size(); i++) {
String fileName = "";
fileName = "http://flashair/thumbnail.cgi?" + directoryName + "/" + fileNames.get(i);
Map<String, Object> entry = new HashMap<String, Object>();
BitmapDrawable drawnIcon = null;
if( (fileName.toLowerCase(Locale.getDefault()).endsWith(".jpg")) || (fileName.toLowerCase(Locale.getDefault()).endsWith(".jpeg")) ) {
Bitmap thumbnail = FlashAirRequest.getBitmap(fileName);
drawnIcon = new BitmapDrawable(getResources(), thumbnail);
}
if(drawnIcon == null) {
entry.put("thmb", R.drawable.ic_launcher);
}
else {
entry.put("thmb", drawnIcon);
}
entry.put("fname", fileNames.get(i)); // Put file name onto the map
data.add(entry);
}
// Set the file list to a widget
listAdapter = new SimpleAdapter(MainActivity.this,
data,
android.R.layout.activity_list_item,
new String[]{"thmb", "fname"},
new int[]{android.R.id.icon, android.R.id.text1});
listAdapter.setViewBinder(new CustomViewBinder());
return listAdapter;
}
@Override
protected void onPostExecute(ListAdapter listAdapter) {
listView = (ListView)findViewById(R.id.listView1);
ColorDrawable divcolor = new ColorDrawable(Color.rgb(17, 19, 58));
listView.setDivider(divcolor);
listView.setDividerHeight(1);
listView.setAdapter(listAdapter);
listView.setOnItemClickListener(MainActivity.this);
}
}.execute(dir);
}
Note that the
thumbnail.cgi
command works exclusively on JPEG files that have EXIF data.
BitmapDrawable
that we saved the thumbnail to is
null
. If this test returns
null
, we assume that the JPEG did not have EXIF data.ListView
. This is where the thumbnail and file name data are bound to
their respective
views. This code also identifies the particular layout elements that the data needs to
be
sent to.
Adapter
and
CustomViewBinder
. These are bound to the
ListView
in lines 90.We will set the list behavior to be the same as it was in Android Tutorial 3: Downloading Contents:
Although the behavior of the list will remain the same, the list has been formatted
differently.
(The
Adapter
we use for the
ListView
is now composed of a list of
Map
objects.)
We will override the original
onItemClick()
function to account for this change:
@Override
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
Object item = l.getItemAtPosition(position); // Get item at clicked position in list of files
if(item instanceof Map<?, ?>) {
Map<String, Object> mapItem = (Map<String, Object>) item;
Object downloadFile = mapItem.get("fname");
if(downloadFile.toString().endsWith("/")) {
// Directory, remove "/" and show content list
String dirName = downloadFile.toString().substring(0, downloadFile.toString().length()-1); // All but the "/"
directoryName = directoryName + "/" + dirName;
listDirectory(directoryName);
}
else if( (downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".jpg")) || (downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".jpeg"))
|| (downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".jpe")) || (downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".png")) ) {
// Image file, download using ImageViewActivity
Intent viewImageIntent = new Intent(this, ImageViewActivity.class);
viewImageIntent.putExtra("downloadFile", downloadFile.toString());
viewImageIntent.putExtra("directoryName", directoryName);
MainActivity.this.startActivity(viewImageIntent);
}
}
}
} // End MainActivity class
This file will be identical to the
class ImageViewActivity
from
Android Tutorial 3: Downloading Contents.
Please refer to that tutorial for an explanation
of the implementation.
android_tutorial_04.zip (533KB)
All sample code on this page is licensed under BSD 2-Clause License